﻿using System.IO;
using Index.Interfaces;

namespace Index.TreeIndex
{


    public class TreeIndexTier : ITreeIndexTier
    {
        public const uint EmptyValue = 0xffffffff;
        private const long nodeSize = 68; // node value + 16 offsets - all uint values
        private const int valueSize = 4;

        private string indexName;
        private int tierNumber;
        private string indexDirectoryPath;
        private FileStream fileStream = null;
        private BinaryReader reader;
        private BinaryWriter writer;



        public uint GetNextTierNodeOffset(uint offset, int digit)
        {
            lock(this)
            {
                long elementOffset = CalculateElementOffset(offset, digit);
                this.fileStream.Seek(elementOffset, SeekOrigin.Begin);
                uint value = this.reader.ReadUInt32();

                return value;
            }
        }

        public uint GetNodeValue(uint offset)
        {
            this.fileStream.Seek(offset, SeekOrigin.Begin);
            uint value = this.reader.ReadUInt32();
            return value;
        }

        public void SetNodeValue(uint offset, uint value)
        {
            this.fileStream.Seek(offset, SeekOrigin.Begin);
            this.writer.Write(value);
            this.writer.Flush();
            this.fileStream.Flush();
        }

        public bool HasNode(uint offset)
        {
            return (this.fileStream.Length - offset) >= nodeSize;
        }

        public uint CreateNode()
        {
            lock(this)
            {
                this.fileStream.Seek(0, SeekOrigin.End);
                uint offset = (uint)this.fileStream.Length;

                for (int k = 0; k < 17; k++) // node value + 16 offsets
                {
                    this.writer.Write(EmptyValue);
                }

                this.writer.Flush();
                this.fileStream.Flush();

                return offset;
            }
        }

        public void SetNextTierNodeOffset(uint offset, int digit, uint value)
        {
            lock (this)
            {
                long elementOffset = CalculateElementOffset(offset, digit);
                this.fileStream.Seek(elementOffset, SeekOrigin.Begin);
                this.writer.Write(value);
                this.writer.Flush();
                this.fileStream.Flush();
            }
        }

        #region ITreeIndexTier Members

        public void Initialize(string indexDirectoryPath, string indexName, int tierNumber)
        {
            this.indexName = indexName;
            this.tierNumber = tierNumber;
            this.indexDirectoryPath = indexDirectoryPath;

            string fileName = CreateFileName();

            this.fileStream = File.Open(fileName, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read);
            this.reader = new BinaryReader(this.fileStream);
            this.writer = new BinaryWriter(this.fileStream);

        }

        public void Cleanup()
        {
            lock (this)
            {
                if(this.fileStream != null)
                {
                    this.writer.Close();
                    this.reader.Close();
                    this.fileStream.Close();

                    this.fileStream = null;
                }
            }
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            Cleanup();
        }

        #endregion

        private string CreateFileName()
        {
            return string.Format("{0}\\{1}-{2:00}.index",this.indexDirectoryPath , this.indexName, this.tierNumber);
        }

        private long CalculateElementOffset(uint offset, int digit)
        {
            long elementOffset = (long)offset + valueSize + digit * valueSize;
            return elementOffset;
        }
    }
}